using System;
using System.Collections;
using System.Data;
using System.Collections.Generic;

namespace FreeFlow.Administration
{
	/// <summary>
	/// A role in a procedure.
	/// </summary>
	public class Role : DatabaseAware, IUpdatable, IRefreshable
	{
		internal Role(Server server, IDataReader reader) : base(server)
		{
			name = reader["eRoleName"].ToString();
      procedureCount = Convert.ToInt32(reader["COUNT"]);
		}

    private string name;
    /// <summary>
    /// Gets the name of the role.
    /// </summary>
    public string Name
    {
      get
      {
        return name;
      }
    }

    private List<string> users;
    private void ReadUsers()
    {
      if (users == null)
      {
        users = new List<string>();
        using (IDataReader reader = Server.ExecuteReader(
          string.Format("SELECT eUserName FROM eAssignment WHERE eRoleName = '{0}'", SqlEscape(name))))
        {
          while (reader.Read())
          {
            users.Add(reader["eUserName"].ToString());
          }
        }
      }
    }

    /// <summary>
    /// Determines whether  this role is held by the specified user.
    /// </summary>
    /// <param name="userName">Name of the user.</param>
    /// <returns>
    /// 	<c>true</c> if the role is held by the specified user; otherwise, <c>false</c>.
    /// </returns>
    public bool IsHeldBy(string userName)
    {
      ReadUsers();
      return users.Contains(userName);
    }

    /// <summary>
    /// Grants this role to the specified user.
    /// </summary>
    /// <param name="userName">Name of the user.</param>
    public void GrantToUser(string userName)
    {
      if (!users.Contains(userName))
        users.Add(userName);
    }

    /// <summary>
    /// Revokes this role from the specified user.
    /// </summary>
    /// <param name="userName">Name of the user.</param>
    public void RevokeFromUser(string userName)
    {
      users.Remove(userName);
    }

    /// <summary>
    /// Applies any assignments for this role.
    /// </summary>
    public void ApplyChanges()
    {
      using (IDbTransaction transaction = Server.BeginTransaction())
      {
        Server.ExecuteNonQuery(transaction, 
          string.Format("DELETE FROM eAssignment WHERE eRoleName = '{0}'", name));

        for (int i = 0; i < users.Count; i++)
        {
          Server.ExecuteNonQuery(transaction, 
            string.Format("INSERT INTO eAssignment (eRoleName, eUserName, eFolderID) VALUES ('{0}', '{1}', ' ')", 
            SqlEscape(name), SqlEscape(users[i].ToString())));
        }

        transaction.Commit();
      }
    }

    private List<string> procedures;
    private void ReadProcedures()
    {
      if (procedures == null)
      {
        procedures = new List<string>();
        using (IDataReader reader = Server.ExecuteReader(
          string.Format("SELECT eProcedureName FROM eRole WHERE eRoleName = '{0}'", SqlEscape(name))))
        {
          while (reader.Read())
          {
            procedures.Add(reader["eProcedureName"].ToString());
          }
        }
      }
    }

    private int procedureCount;
    /// <summary>
    /// Gets the number of procedures in which this role is defined.
    /// </summary>
    public int ProcedureCount
    {
      get
      {
        if ((procedures == null) && (procedureCount > 0))
          return procedureCount;
        else
        {
          ReadProcedures();
          return procedures.Count;
        }
      }
    }

    /// <summary>
    /// Gets the name of the procedure at the specified index.
    /// </summary>
    /// <param name="index">The index.</param>
    public string Procedure(int index)
    {
      ReadProcedures();
      // it may be the procedure has been deleted since the count was calculated so return null if nothing there
      if ((index >= 0) && (index < procedures.Count)) 
        return (string)procedures[index];
      else
        return null;
    }

    /// <summary>
    /// Gets the number of users that hold this role.
    /// </summary>
    public int HeldByUsersCount
    {
      get
      {
        ReadUsers();
        return users.Count;
      }
    }

    #region IRefreshable Members

    /// <summary>
    /// Refreshes the data for this role.
    /// </summary>
    public void Refresh()
    {
      users = null;
      procedures = null;
      procedureCount = 0;
    }

    #endregion
  }
}
